// 13.6 对象移动
/**
 * 新标准的一个最主要的特性是可以移动而非拷贝对象的能力。如我们在13.1.1节（第440页）中所见，很多情况下都会发生对象拷贝。
 * 在其中某些情况下，对象拷贝后就立即被销毁了。在这些情况下，移动而非拷贝对象会大幅度提升性能。
 * 使用移动而不是拷贝的另一个原因源于IO类或unique_ptr这样的类。这些类都包含不能被共享的资源（如指针或IO缓冲）。因此，这些类型的对象不能拷贝但可以移动。
 * 标准库容器、string和shared_ptr类既支持移动也支持拷贝。IO类和unique_ptr类可以移动但不能拷贝。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;

int main()
{
    // 13.6.1 右值引用

    // 为了支持移动操作，新标准引入了一种新的引用类型——右值引用（rvalue reference）。所谓右值引用就是必须绑定到右值的引用。我们通过&&而不是&来获得右值引用。
    // 如我们将要看到的，右值引用有一个重要的性质————只能绑定到一个将要销毁的对象。因此，我们可以自由地将一个右值引用的资源“移动”到另一个对象中。
    // 左值和右值是表达式的属性（参见4.1.1节，第121页）。一般而言，一个左值表达式表示的是一个对象的身份，而一个右值表达式表示的是对象的值。
    // 类似任何引用，一个右值引用也不过是某个对象的另一个名字而已。如我们所知，对于常规引用（为了与右值引用区分开来，我们可以称之为左值引用（lvalue reference）），
    // 我们不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式（参见2.3.1节，第46页）。
    // 右值引用有着完全相反的绑定特性：我们可以将一个右值引用绑定到这类表达式上，但不能将一个右值引用直接绑定到一个左值上：
    int i = 42;
    int &r = i;         // 正确，r引用i
    //int &&rr = i;     // 错误，不能将一个右值引用绑定到一个左值上
    //int &r2 = i * 42; // 错误，i*42是一个右值
    const int &r3 = i*42; // 正确，我们可以将一个const的引用绑定到一个右值上
    int &&rr2 = i * 42;  // 正确，将rr2绑定到乘法结果上
    // 返回非引用类型的函数，连同算术、关系、位以及后置递增/递减运算符，都生成右值。我们不能将一个左值引用绑定到这类表达式上，但我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。

    // 左值持久，右值短暂
    // 左值有持久的状态，而右值要么是字面常量，要么是在表达式求值过程中创建的临时对象。
    // 由于右值引用只能绑定到临时对象，我们得知：1.所引用的对象将要被销毁 2.该对象没有其他用户
    // 这两个特性意味着：使用右值引用的代码可以自由地接管所引用的对象的资源。
    // 右值引用指向将要被销毁的对象。因此，我们可以从绑定到右值引用的对象“窃取”状态。

    // 变量是左值
    // 变量表达式都是左值。带来的结果就是，我们不能将一个右值引用绑定到一个右值引用类型的变量上，这有些令人惊讶：
    int &&rr11 = 42;     // 正确，字面常量是右值
    //int &&rr22 = rr11; // 错误，表达式rr11是左值！
    // 变量是左值，因此我们不能将一个右值引用直接绑定到一个变量上，即使这个变量是右值引用类型也不行。

    // 标准库move函数
    // 虽然不能将一个右值引用直接绑定到一个左值上，但我们可以显式地将一个左值转换为对应的右值引用类型。
    // 我们还可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用，此函数定义在头文件utility中。
    // move函数使用了我们将在16.2.6节（第610页）中描述的机制来返回给定对象的右值引用。
    int &&rr33 = std::move(rr11); // ok
    // move调用告诉编译器：我们有一个左值，但我们希望像一个右值一样处理它。
    // 我们必须认识到，调用move就意味着承诺：除了对rr11赋值或销毁它外，我们将不再使用它。在调用move之后，我们不能对移后源对象的值做任何假设。
    // 我们可以销毁一个移后源对象，也可以赋予它新值，但不能使用一个移后源对象的值。
    // 使用move的代码应该使用std：：move而不是move。这样做可以避免潜在的名字冲突。

    

    
    return 0;
}